package cn.edu.jxau.util;

import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * 数组队列又叫循环队列，为了提高空间效率，使用{@code head}和
 * {@code tail}两个索引维护一个队列，要点如下。
 * 1.判断队列是否为空：head == tail
 * 2.判断队列是否已满：(tail+1)%arr.length == head
 * 3.队列元素个数：(tail-head+arr.length)%arr.length;
 * @author 付大石
 */
public class ArrayQueue<T> extends Queue<T> {

    /**
     * 队列
     */
    private T[] arr;

    /**
     * 队列原始容量
     */
    private static final int INIT_CAPACITY = 10;

    /**
     * 队列尾部元素的下一个位置的索引
     */
    private int tail;

    /**
     * 队列头部元素索引
     */
    private int head;

    public ArrayQueue() {

        arr = (T[]) new Object[INIT_CAPACITY];
    }

    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {

            private int cursor = head;

            @Override
            public boolean hasNext() {
                return cursor != tail;
            }

            @Override
            public T next() {
                if (!hasNext()) {
                    throw new NoSuchElementException("迭代器所处位置无元素");
                }
                T item = arr[cursor];
                cursor = (cursor + 1) % arr.length;
                return item;
            }
        };
    }

    @Override
    public int size() {
        return (tail - head + arr.length) % arr.length;
    }

    @Override
    public void enqueue(T item) {

        if ((tail + 1) % arr.length == head) { // 队列已满，扩容容量
            resize(arr.length * 2);
        }

        // 插入元素 //
        arr[tail] = item;
        tail = (tail + 1) % arr.length;
    }

    /**
     * 重新调整循环队列的大小
     * @param newCapacity
     */
    private void resize(int newCapacity) {

        if (newCapacity < INIT_CAPACITY) { // 避免缩减得太小
            return;
        }
        System.out.printf("resize to %d\n", newCapacity);
        T[] tempArr = (T[]) new Object[newCapacity];
        int size = size();
        int h = head;
        for (int i = 0; i < size; i++) {
            tempArr[i] = arr[h];
            h = (h + 1) % arr.length;
        }
        head = 0;
        tail = size;
        arr = tempArr;
    }

    @Override
    public T dequeue() {

        T item = look();
        arr[head] = null;
        head = (head + 1) % arr.length;
        int size = size();
        if((size+1)*4 < arr.length) {
            resize(arr.length /2);
        }
        return item;
    }

    @Override
    public T look() {

        if (isEmpty()) {
            return null;
        }
        return arr[head];
    }

    @Override
    public String toString() {
        
        StringBuilder stringBuilder = new StringBuilder();
        int size = size();
        stringBuilder.append("queue size=").append(size).append("\t");
        stringBuilder.append("[");
        if (size == 0) {
            stringBuilder.append("]");
        } else {
            for (int i = head; i < size; i = (i + 1) % arr.length) {
                stringBuilder.append(arr[i]).append(",");
            }
            stringBuilder.deleteCharAt(stringBuilder.length() - 1).append("]");
        }
        return stringBuilder.toString();
    }
}
